package com;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SingleFileHTTPServer {

    private static final Logger logger = Logger.getLogger("SingleFileHTTPServer");

    private final static int THREAD_NUM = 100;

    private final byte[] header;

    private final byte[] content;

    private final int port;

    private final String encoding;

    public SingleFileHTTPServer(String data,String encoding,String mimeType,int port) throws UnsupportedEncodingException {

        this(data.getBytes(encoding),encoding,mimeType,port);

    }

    public SingleFileHTTPServer(byte[] data,String encoding,String mimeType,int port){

        this.content = data;

        this.encoding = encoding;

        this.port = port;

        String header = "HTTP/1.0 200 OK\r\n"
                + "Server: OneFile 2.0\r\n"
                + "Content-length: " + this.content.length + "\r\n"
                + "Content-type: " + mimeType + "; charset=" + encoding + "\r\n\r\n";

        this.header = header.getBytes(Charset.forName("US-ASCII"));

    }

    public void start(){

        ExecutorService tp = Executors.newFixedThreadPool(THREAD_NUM);

        try (ServerSocket serverSocket = new ServerSocket(port)){

            logger.info("Accepting connections on port" + serverSocket.getLocalPort());

            logger.info("Data to be start.");

            logger.info(new String(this.content,encoding));

            while (true){

                try {

                    Socket socket = serverSocket.accept();

                    logger.info("connected with " + socket);

                    tp.submit(new HTTPHandler(socket));
                }catch (RuntimeException e){
                    logger.log(Level.SEVERE,"Unexcepted error",e);
                }

            }

        } catch (IOException e) {
            logger.log(Level.WARNING,"Exception accepting connection",e);
        }catch (RuntimeException ex){
            logger.log(Level.SEVERE,"Couldn't start server");
        }

    }

    public class HTTPHandler implements Callable<Void>{

        Socket conn;

        public HTTPHandler(Socket conn) {
            this.conn = conn;
        }

        @Override
        public Void call(){

            try {

                InputStream in = new BufferedInputStream(conn.getInputStream());

                OutputStream out = new BufferedOutputStream(conn.getOutputStream());

                StringBuilder request = new StringBuilder(80);

                while (true){

                    int c = in.read();

                    if( c == '\r' || c == '\n' || c == -1) break;

                    request.append((char)c);

                }

                if(request.toString().indexOf("HTTP/") != -1){
                    out.write(header);
                }

                out.write(content);

                out.flush();

            } catch (IOException e) {
                logger.log(Level.WARNING,"Error writing to cliend",e);
            } finally {

                if(conn != null) {
                    try {
                        conn.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }

            return null;
        }
    }

    public static void main(String[] args) {

        int port;

        try{

            port = Integer.parseInt(args[1]);

            if(port < 1 || port > 65535) port = 80;

        }catch (RuntimeException e){

            port = 80;

        }

        String encoding = "UTF-8";

        if(args.length > 2) encoding = args[2];

        try {
            Path path = Paths.get(args[0]);
            byte[] data = Files.readAllBytes(path);

            String contentType = URLConnection.getFileNameMap().getContentTypeFor(args[0]);

            SingleFileHTTPServer server = new SingleFileHTTPServer(data,encoding,contentType,port);

            server.start();

        } catch (IOException e) {
            logger.severe(e.getMessage());
        }

    }
}
